package com.swl.juc.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/*
重入锁ReentrantLock是排他锁，排他锁在同一时刻仅有一个线程可以进行访问，但是在大多数场景下，大部分时间都是提供读服务，而写服务占有的时间较少。
然而读服务不存在数据竞争问题，如果一个线程在读时禁止其他线程读势必会导致性能降低。所以就提供了读写锁。
读写锁维护着一对锁，一个读锁和一个写锁。通过分离读锁和写锁，使得并发性比一般的排他锁有了较大的提升：在同一时间可以允许多个读线程同时访问，但是在写线程访问时，所有读线程和写线程都会被阻塞。
 读写锁的主要特性：

公平性：支持公平性和非公平性。
重入性：支持重入。读写锁最多支持65535个递归写入锁和65535个递归读取锁。
锁降级：遵循获取写锁、获取读锁在释放写锁的次序，写锁能够降级成为读锁
读写锁ReentrantReadWriteLock实现接口ReadWriteLock，该接口维护了一对相关的锁，一个用于只读操作，另一个用于写入操作。只要没有 writer，读取锁可以由多个 reader 线程同时保持。
写入锁是独占的。
获取锁是共享的。
*/
public class ReadWriterLockTest {
    private static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    private static ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
    private static ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
    private int number = 0;

    //读(可以多个线程同时操作)
    public void get() {
        readLock.lock();//上锁
        try {
            System.out.println(Thread.currentThread().getName() + ":" + "开始读取");
            try {
                TimeUnit.MILLISECONDS.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":" + "读取完毕");
        } finally {
            readLock.unlock();//释放锁
        }
    }

    //写(一次只能有一个线程操作)
    public void set(int number) {
        writeLock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+"开始写入");
            this.number += 5;
            try {
                TimeUnit.MILLISECONDS.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"写入完毕");
        } finally {
            writeLock.unlock();
        }
    }

    public static void main(String[] args) {
        ReadWriterLockTest readWriterLockTest = new ReadWriterLockTest();
        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {//一个线程写
                @Override
                public void run() {
                    readWriterLockTest.set((int) Math.random() * 101);
                }
            }, "线程写入" + i).start();
        }

        for (int i = 0; i < 100; i++) {//100个线程读
            new Thread(() -> {
                readWriterLockTest.get();
            }, "线程读取" + i).start();

            new Thread(() -> {
                readWriterLockTest.get();
            }, "线程读取" + i).start();
        }


    }
}

